// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <pollux/common/file/file.h>
#include <pollux/common/memory/memory_pool.h>

namespace Aws::S3 {
class S3Client;
}

namespace kumo::pollux::filesystems {

/// S3WriteFile uses the Apache Arrow implementation as a reference.
/// AWS C++ SDK allows streaming writes via the MultiPart upload API.
/// Multipart upload allows you to upload a single object as a set of parts.
/// Each part is a contiguous portion of the object's data.
/// While AWS and Minio support different sizes for each
/// part (only requiring a minimum of 5MB), Cloudflare R2 requires that every
/// part be exactly equal (except for the last part). We set this to 10 MiB, so
/// that in combination with the maximum number of parts of 10,000, this gives a
/// file limit of 100k MiB (or about 98 GiB).
/// (see https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html)
/// (for rational, see: https://github.com/apache/arrow/issues/34363)
/// You can upload these object parts independently and in any order.
/// After all parts of your object are uploaded, Amazon S3 assembles these parts
/// and creates the object.
/// https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html
/// https://github.com/apache/arrow/blob/main/cpp/src/arrow/filesystem/s3fs.cc
/// S3WriteFile is not thread-safe.
/// UploadPart is currently synchronous during append and flush.
/// TODO: Evaluate and add option for asynchronous part uploads.
/// TODO: Implement retry on failure.
class S3WriteFile : public WriteFile {
 public:
  S3WriteFile(
      std::string_view path,
      Aws::S3::S3Client* client,
      memory::MemoryPool* pool);

  /// Appends data to the end of the file.
  /// Uploads a part on reaching part size limit.
  void append(std::string_view data) override;

  /// No-op. Append handles the flush.
  void flush() override;

  /// Close the file. Any cleanup (disk flush, etc.) will be done here.
  void close() override;

  /// Current file size, i.e. the sum of all previous Appends.
  uint64_t size() const override;

  /// Return the number of parts uploaded so far.
  int numPartsUploaded() const;

 protected:
  class Impl;
  std::shared_ptr<Impl> impl_;
};

} // namespace kumo::pollux::filesystems
